"""
Ingress Manager for WebService

Handles creation and management of Kubernetes Ingress resources.
"""

import logging
from typing import Optional, Dict, Any
from kubernetes import client
from kubernetes.client.rest import ApiException

from .api import WebServiceSpec, IngressStatus
from common import consts


class IngressManagerError(Exception):
    """Ingress manager error"""
    pass


class IngressManager:
    """Kubernetes Ingress resource management"""

    def __init__(self):
        """Initialize ingress manager"""
        self.logger = logging.getLogger(__name__)
        self.networking_v1 = client.NetworkingV1Api()
        self.v1 = client.CoreV1Api()

    def create_ingress(self, webservice_spec: WebServiceSpec, webservice_name: str, webservice_namespace: str) -> str:
        """
        Create Ingress resource
        
        Args:
            webservice_spec: WebService specification object
            webservice_name: Name of the WebService for reference
            webservice_namespace: Namespace of the WebService
            
        Returns:
            Name of the created Ingress resource
            
        Raises:
            IngressManagerError: If ingress creation fails
        """
        try:
            ingress_name = self._generate_ingress_name(webservice_name)

            self.logger.info(f"Creating ingress {ingress_name} for WebService {webservice_name}")

            # Create ingress object
            ingress = self._build_ingress_object(
                webservice_spec, ingress_name, webservice_name, webservice_namespace
            )

            # Create ingress in Kubernetes
            created_ingress = self.networking_v1.create_namespaced_ingress(
                namespace=webservice_namespace,
                body=ingress
            )

            self.logger.info(f"Successfully created ingress {ingress_name}")
            return ingress_name

        except ApiException as e:
            error_msg = f"Failed to create ingress {ingress_name}: {e}"
            self.logger.error(error_msg)
            raise IngressManagerError(error_msg) from e
        except Exception as e:
            error_msg = f"Unexpected error creating ingress {ingress_name}: {e}"
            self.logger.error(error_msg)
            raise IngressManagerError(error_msg) from e

    def update_ingress(self, webservice_spec: WebServiceSpec, webservice_name: str, webservice_namespace: str) -> str:
        """
        Update existing Ingress resource
        
        Args:
            webservice_spec: WebService specification object
            webservice_name: Name of the WebService for reference
            webservice_namespace: Namespace of the WebService
            
        Returns:
            Name of the updated Ingress resource
            
        Raises:
            IngressManagerError: If ingress update fails
        """
        try:
            ingress_name = self._generate_ingress_name(webservice_name)

            self.logger.info(f"Updating ingress {ingress_name} for WebService {webservice_name}")

            # Get existing ingress
            existing_ingress = self.networking_v1.read_namespaced_ingress(
                name=ingress_name,
                namespace=webservice_namespace
            )

            # Build new ingress object
            new_ingress = self._build_ingress_object(
                webservice_spec, ingress_name, webservice_name, webservice_namespace
            )

            # Update ingress in Kubernetes
            updated_ingress = self.networking_v1.patch_namespaced_ingress(
                name=ingress_name,
                namespace=webservice_namespace,
                body=new_ingress
            )

            self.logger.info(f"Successfully updated ingress {ingress_name}")
            return ingress_name

        except ApiException as e:
            error_msg = f"Failed to update ingress {ingress_name}: {e}"
            self.logger.error(error_msg)
            raise IngressManagerError(error_msg) from e
        except Exception as e:
            error_msg = f"Unexpected error updating ingress {ingress_name}: {e}"
            self.logger.error(error_msg)
            raise IngressManagerError(error_msg) from e

    def delete_ingress(self, webservice_name: str, webservice_namespace: str) -> bool:
        """
        Delete Ingress resource
        
        Args:
            webservice_name: Name of the WebService for reference
            webservice_namespace: Namespace of the WebService
            
        Returns:
            True if deletion was successful
            
        Raises:
            IngressManagerError: If ingress deletion fails
        """
        try:
            ingress_name = self._generate_ingress_name(webservice_name)

            self.logger.info(f"Deleting ingress {ingress_name} for WebService {webservice_name}")

            # Delete ingress in Kubernetes
            self.networking_v1.delete_namespaced_ingress(
                name=ingress_name,
                namespace=webservice_namespace
            )

            self.logger.info(f"Successfully deleted ingress {ingress_name}")
            return True

        except ApiException as e:
            if e.status == 404:
                self.logger.info(f"Ingress {ingress_name} not found, already deleted")
                return True
            error_msg = f"Failed to delete ingress {ingress_name}: {e}"
            self.logger.error(error_msg)
            raise IngressManagerError(error_msg) from e
        except Exception as e:
            error_msg = f"Unexpected error deleting ingress {ingress_name}: {e}"
            self.logger.error(error_msg)
            raise IngressManagerError(error_msg) from e

    def get_ingress_status(self, webservice_name: str, webservice_namespace: str) -> str:
        """
        Get Ingress status
        
        Args:
            webservice_name: Name of the WebService for reference
            webservice_namespace: Namespace of the WebService
            
        Returns:
            Status of the ingress
        """
        try:
            ingress_name = self._generate_ingress_name(webservice_name)

            # Get ingress object
            ingress = self.networking_v1.read_namespaced_ingress(
                name=ingress_name,
                namespace=webservice_namespace
            )

            # Check if ingress exists
            if not ingress:
                return IngressStatus.UNHEALTHY.value

            # Check ingress status
            status = ingress.status

            # Check if ingress has LoadBalancer status
            if not status or not status.load_balancer:
                return IngressStatus.PENDING.value
            
            return IngressStatus.HEALTHY.value

        except ApiException as e:
            if e.status == 404:
                self.logger.info(f"Ingress {ingress_name} not found, unhealthy state")
                return IngressStatus.UNHEALTHY.value
            else:
                self.logger.error(f"API error getting ingress status: {e}")
                return IngressStatus.UNKNOWN.value
        except Exception as e:
            self.logger.error(f"Unexpected error getting ingress status: {e}")
            return IngressStatus.UNKNOWN.value

    def _generate_ingress_name(self, webservice_name: str) -> str:
        """Generate ingress name from WebService name"""
        return f"{webservice_name}-ingress"

    def _build_ingress_object(self, webservice_spec: WebServiceSpec, ingress_name: str,
                              webservice_name: str, webservice_namespace: str) -> Dict:
        """Build Kubernetes Ingress object"""
        ingress_spec = webservice_spec.ingress
        service_name = self._generate_service_name(webservice_name)

        # Build rules
        rules = []
        for rule in ingress_spec.rules:
            rule_dict = {
                "http": {
                    "paths": []
                }
            }

            for path in rule.http.paths:
                path_dict = {
                    "path": path.path,
                    "pathType": path.path_type.value if path.path_type else "Prefix",
                    "backend": {
                        "service": {
                            "name": path.backend.service.name,
                            "port": {
                                "number": path.backend.service.port
                            }
                        }
                    }
                }
                rule_dict["http"]["paths"].append(path_dict)

            rules.append(rule_dict)

        # Build ingress object
        ingress = {
            "apiVersion": "networking.k8s.io/v1",
            "kind": "Ingress",
            "metadata": {
                "name": ingress_name,
                "namespace": webservice_namespace,
                "labels": {
                    "app": webservice_name,
                    "webservice": webservice_name,
                    "managed-by": consts.APPLICATION_NAME
                }
            },
            "spec": {
                "rules": rules
            }
        }

        return ingress

    def _get_backend_service_name(self, ingress) -> str:
        """Get backend service name from ingress"""
        if (ingress.spec.rules and
                ingress.spec.rules[0].http and
                ingress.spec.rules[0].http.paths and
                ingress.spec.rules[0].http.paths[0].backend.service):
            return ingress.spec.rules[0].http.paths[0].backend.service.name
        return None

    def _generate_service_name(self, webservice_name: str) -> str:
        """Generate service name from WebService name"""
        return f"{webservice_name}-service"
